package com.cctc.juc.source.Bitc.Faqs;
/**
 * 面试题：
 * 阿里社招二面：谈谈你对 JUC 中 AQS 的理解，用了什么设计模式？为什么它是锁的灵魂？
 * - https://mp.weixin.qq.com/s/7Nd6UfuZGqJN8R__ux5Zeg
 * 【 AQS（Abstract Queued Synchronizer）抽象队列同步器】
 * 1. 简介：AQS 是一个用于构建显式锁以及同步容器的基类，底层基于 CAS 自旋操作进行实现，主要用于弥补 synchronized 关键字的不足之处，成为构建：
 * 1）JUC 显式锁：
 * - 独占锁：ReentrantLock、LockSupport；
 * - 共享锁：ReadLock、Semaphore、CountDownLatch、CyclicBarrier、Phaser；
 * - 组合锁：ReentrantReadWriteLock、StampedLock；
 * 2）JUC 阻塞队列
 * 3）线程池：ThreadPoolExecutor；
 * 4）异步回调工具：FutureTask；
 * 等等的 JUC 并发工具包的底层支撑框架。
 * 同样，我们也能继承 AQS 类去实现满足自己需求的同步工具（或锁）。
 * 2. AQS 模板流程（使用层面）
 * AQS 是基于模板方法设计模式进行实现的，是模板方法设计模式的一个经典运用案例。
 * AQS 为不同的资源访问方式提供了不同的模板流程，包括独占锁、共享锁模板流程。
 * 这些模板流程完成了线程进出阻塞队列的通用逻辑，例如 “自旋” 入队、启动 “自旋” 抢锁流程等。
 * 上层同步组件，例如显式锁，JUC 阻塞队列、线程池等，只需要重写模板流程中的钩子方法，实现共享资源（state）的获取与释放逻辑即可，AQS 在执行模板流程时会回调这些钩子方法，以完成自定义逻辑想要达成的效果。
 * AQS 模板流程中的钩子方法包括：
 * - tryAcquire(int)、tryRelease(int)
 * - tryAcquireShared(int)、tryReleaseShared(int)
 * - isHeldExclusively()
 * 3. AQS 线程同步（抢锁）原理（[源码] 实现层面）
 * AQS 内部维护了一个基于双向链表实现的 FIFO 同步队列来完成抢锁线程的排队工作，内部通过 head 和 tail 节点记录队首和队尾节点，每个节点的类型为 Node。
 * - 当线程抢锁失败后，AQS 会将该抢锁线程封装成一个 Node 节点，并通过 CAS “自旋” 入队操作将其加入到该队列的尾部，然后开启 “自旋” 抢锁流程。同时，为了不浪费 “空自旋” 造成的 CPU 资源浪费问题，Node 节点会在 “自旋” 过程中进行自我阻塞，等待被前驱节点唤醒后再重启循环，继续执行验证逻辑；
 * - 当队首节点线程释放锁时，AQS 会尝试唤醒其后驱节点线程，重启 “自旋” 抢锁流程去占有锁，以达到线程同步的目的。
 * 4. 总结
 * AQS 是基于 CAS “自旋” 实现的轻量级线程同步基类，主要是为了弥补 synchronized 重量级锁的不足，例如：
 * 1）因为 Java 对象内置锁是由 JVM 底层自动完成的，应用程序无法主动释放锁，而且抢锁过程是永久阻塞式、不可中断式的，使用起来不太灵活。
 * 2）功能太单一，不具备一些比较高级的功能，例如：非阻塞抢锁、限时抢锁、可中断抢锁，以及维护多个条件队列，以提高锁的使用效率等。
 * 2）在锁竞争激烈的情况下，将会膨胀为重量级锁，该锁是基于 Linux 内核中的 Mutex Lock 互斥锁实现的，抢锁线程的阻塞与唤醒需要 CPU 在用户态和核心态之间频繁切换，导致其性能急剧下降。
 * 本质上，AQS 其实就是将 “内核态” 的 [重量级锁] 在 Java 应用层面使用 CAS “自旋” 显式实现了一遍，并通过线程等待与唤醒机制解决了在竞争激烈的场景下 CAS “空自旋” 所来带的 CPU 性能损耗问题，同时又增加了一些高级锁功能，例如非阻塞抢锁、限时阻塞抢锁、可中断抢锁等，以及维护多个条件队列，以提高锁的使用效率等，例如：
 * 1）基于双向链表构建了一个线程同步队列，对标 EntryList，存放（不可中断式阻塞）排队线程，同时又在此基础上增加了非阻塞抢锁、限时阻塞抢锁以及可中断抢锁等高级锁功能，最后通过线程等待与唤醒机制完美地解决了 CAS “空自旋” 所来带的 CPU 性能损耗问题；
 * 2）基于 Condition 接口构建的条件队列，对标 WaitSet，相比使用 Object 的 wait()/notify[All]()，使用 Condition 的 await()/signal[All]() 方法实现线程同步更加高效，且支持维护多个条件队列，提高了锁的使用效率。
 * 总的来说，AQS 是一个用于构建 JUC 显式锁以及同步容器的基类，内部采用模板模式进行实现，为不同的资源共享方式提供了不同的模板流程，包括独占锁、共享锁模板流程，这些模板流程完成了线程排队的通用逻辑，例如 “自旋” 入队、启动 “自旋” 抢锁流程等，上层同步组件，例如 JUC 显式锁，阻塞队列、线程池、线程同步工具、异步回调工具等，只需要重写模板流程中的钩子方法，实现同步资源（state）的获取与释放逻辑即可，AQS 在执行模板流程时会回调这些钩子方法，从而达到线程同步的目的。
 */